#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/kernel/threadmgr.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/power.h>
#include <psp2/apputil.h>
#include <psp2/ctrl.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/system_param.h>
#include <psp2/appmgr.h>
#include <psp2/shellutil.h>
#include <psp2/io/fcntl.h>
#include <psp2/io/devctl.h>
#include <psp2/vshbridge.h>
#include <psp2/touch.h>
#include <psp2/common_dialog.h>

#include <taihen.h>
#include <pthread.h>

#include <vita2d.h>
#include "vita2d_ext.h"

#include "init.h"
#include "utils.h"
#include "file.h"
#include "ui.h"
#include "config.h"
#include "retro.h"
#include "menu.h"
#include "main.h"
#include "boot.h"

#ifdef SCE_LIBC_SIZE
unsigned int sceLibcHeapSize = SCE_LIBC_SIZE;
#endif

// int _newlib_heap_size_user = 192 * 1024 * 1024;

char app_titleid[12];
char app_raw_path[MAX_PATH_LENGTH];

int is_safe_mode = 0;
int is_vitatv_model = 0;

int language = 0, enter_button = 0, date_format = 0, time_format = 0;

extern int main_core_entries_enable;

int isSafeMode()
{
    return is_safe_mode;
}

int checkSafeMode()
{
    if (sceIoDevctl("ux0:", 0x3001, NULL, 0, NULL, 0) == 0x80010030)
        is_safe_mode = 1;

    return is_safe_mode;
}

int isVitatvModel()
{
    return is_vitatv_model;
}

int checkVitatvModel()
{
    if (sceKernelGetModel() == SCE_KERNEL_MODEL_VITATV)
        is_vitatv_model = 1;

    return is_vitatv_model;
}

static void initVita2dLib()
{
    vita2d_init_advanced(4 * 1024 * 1024);
    vita2d_ext_init(vita2d_get_context(), vita2d_get_shader_patcher());
    vita2d_set_vblank_wait(0);
}

static void finishVita2dLib()
{
    vita2d_ext_fini();
    vita2d_fini();
}

static void initSceAppUtil()
{
    // Init SceAppUtil
    SceAppUtilInitParam init_param;
    SceAppUtilBootParam boot_param;
    memset(&init_param, 0, sizeof(SceAppUtilInitParam));
    memset(&boot_param, 0, sizeof(SceAppUtilBootParam));
    sceAppUtilInit(&init_param, &boot_param);

    // System params
    sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, &language);
    sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_ENTER_BUTTON, &enter_button);
    sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_DATE_FORMAT, &date_format);
    sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_TIME_FORMAT, &time_format);

    // Set common dialog config
    SceCommonDialogConfigParam config;
    sceCommonDialogConfigParamInit(&config);
    sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, (int *)&config.language);
    sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_ENTER_BUTTON, (int *)&config.enterButtonAssign);
    sceCommonDialogSetConfigParam(&config);
}

static void finishSceAppUtil()
{
    // Shutdown AppUtil
    sceAppUtilShutdown();
}

int initMain(int argc, char *const argv[])
{
    sceIoRemove(APP_LOG_PATH);
    sceIoRemove(CORE_LOG_PATH);

    loadSettingConfig(CONFIG_TYPE_MAIN);
    loadGraphicsConfig(CONFIG_TYPE_MAIN);
    loadControlConfig(CONFIG_TYPE_MAIN);
    loadMiscConfig(CONFIG_TYPE_MAIN);

    initBootParams(argc, argv);
    initConfigs();

    APP_LOG("initMain...\n");

    createFolder(APP_DATA_DIR);

    scePowerSetArmClockFrequency(444);
    scePowerSetBusClockFrequency(222);
    scePowerSetGpuClockFrequency(222);
    scePowerSetGpuXbarClockFrequency(166);

    // Init SceShellUtil events
    sceShellUtilInitEvents(0);

    // Init SceAppUtil
    initSceAppUtil();

    // Get titleid
    memset(app_titleid, 0, sizeof(app_titleid));
    sceAppMgrAppParamGetString(sceKernelGetProcessId(), 12, app_titleid, sizeof(app_titleid));
    APP_LOG("app_titleid: %s\n", app_titleid);

    // Get app root raw dir
    memset(app_raw_path, 0, sizeof(app_raw_path));
    sceAppMgrGetRawPath("app0:", app_raw_path, sizeof(app_raw_path));
    APP_LOG("app_raw_path: %s\n", app_raw_path);

    sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG);
    sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START);
    sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK, SCE_TOUCH_SAMPLING_STATE_START);

    checkVitatvModel();

    pthread_init();

    initVita2dLib();
    initRetro();
    initUi();

    // sceAppMgrUmount(app_raw_path);

    if (checkSafeMode())
    {
        APP_LOG("Is safe mode\n");
        APP_LOG("initMain failed\n");
        printSafeMode();
    }
    if (core_entries)
    {
        setMainCoreEntiesEnable(1);
    }

    // Lock USB connection and PS button
    sceShellUtilLock(SCE_SHELL_UTIL_LOCK_TYPE_USB_CONNECTION | SCE_SHELL_UTIL_LOCK_TYPE_PS_BTN_2);

    APP_LOG("initMain done\n");

    if (exec_boot_mode == BOOT_MODE_GAME)
        loadBootGame();

    return 0;
}

int finishMain()
{
    pthread_terminate();
    finishUi();
    finishRetro();
    finishVita2dLib();
    finishSceAppUtil();

    return 0;
}
